import * as Constants from '../constants';
import {Point} from '../type';

/**
 * 计算两个坐标之间的距离
 * @param point1    第一个点
 * @param point2    第二个点
 */
export const mathDistance = (point1: Point, point2: Point): number => {
    return Math.sqrt(Math.pow((point1[0] - point2[0]), 2) + Math.pow((point1[1] - point2[1]), 2));
};

/**
 * 计算点集合的总距离
 * @param points
 * @returns {number}
 */
export const wholeDistance = (points: Array<Point>): number => {
    let distance: number = 0;
    if (points && Array.isArray(points) && points.length > 0) {
        points.forEach((item: Point, index: number) => {
            if (index < points.length - 1) {
                distance += (mathDistance(item, points[index + 1]));
            }
        });
    }
    return distance;
};

/**
 * 获取基础长度
 * @param points
 * @returns {number}
 */
export const getBaseLength = (points: Array<Point>): number => {
    return Math.pow(wholeDistance(points), 0.99);
};

/**
 * 求取两个坐标的中间值
 * @param point1
 * @param point2
 * @returns {[*,*]}
 * @constructor
 */
export const Mid = (point1: Point, point2: Point): Point => {
    return [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2];
};

/**
 * 通过三个点确定一个圆的中心点
 * @param point1
 * @param point2
 * @param point3
 */
export const getCircleCenterOfThreePoints = (point1: Point, point2: Point, point3: Point): Point => {
    let pntA: Point = [(point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2];
    let pntB: Point = [pntA[0] - point1[1] + point2[1], pntA[1] + point1[0] - point2[0]];
    let pntC: Point = [(point1[0] + point3[0]) / 2, (point1[1] + point3[1]) / 2];
    let pntD: Point = [pntC[0] - point1[1] + point3[1], pntC[1] + point1[0] - point3[0]];
    return getIntersectPoint(pntA, pntB, pntC, pntD);
};

/**
 * 获取交集的点
 * @param pntA
 * @param pntB
 * @param pntC
 * @param pntD
 * @returns {[*,*]}
 */
export const getIntersectPoint = (pntA: Point, pntB: Point, pntC: Point, pntD: Point): Point => {
    if (pntA[1] === pntB[1]) {
        let f: number = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1]);
        let x: number = f * (pntA[1] - pntC[1]) + pntC[0];
        let y: number = pntA[1];
        return [x, y];
    }
    if (pntC[1] === pntD[1]) {
        let e: number = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1]);
        let x: number = e * (pntC[1] - pntA[1]) + pntA[0];
        let y: number = pntC[1];
        return [x, y];
    }
    let e: number = (pntB[0] - pntA[0]) / (pntB[1] - pntA[1]);
    let f: number = (pntD[0] - pntC[0]) / (pntD[1] - pntC[1]);
    let y: number = (e * pntA[1] - pntA[0] - f * pntC[1] + pntC[0]) / (e - f);
    let x: number = e * y - e * pntA[1] + pntA[0];
    return [x, y];
};

/**
 * 获取方位角（地平经度）
 * @param startPoint
 * @param endPoint
 * @returns {*}
 */
export const getAzimuth = (startPoint: Point, endPoint: Point): number => {
    let azimuth: number = 0;
    // todo 计算angle需要使用3857坐标系的坐标，此处需要进行转换。后期需要根据参数来判断是否需要转换
    /*// @ts-ignore
    const tempStartPoint: Point = ol.proj.transform(startPoint, 'EPSG:4326', 'EPSG:3857');
    // @ts-ignore
    const tempEndPoint: Point = ol.proj.transform(endPoint, 'EPSG:4326', 'EPSG:3857');
    let angle: number = Math.asin(Math.abs(tempEndPoint[1] - tempStartPoint[1]) / (mathDistance(tempStartPoint, tempEndPoint)));*/
    let angle: number = Math.asin(Math.abs(endPoint[1] - startPoint[1]) / (mathDistance(startPoint, endPoint)));
    if (endPoint[1] >= startPoint[1] && endPoint[0] >= startPoint[0]) {
        azimuth = angle + Math.PI;
    } else if (endPoint[1] >= startPoint[1] && endPoint[0] < startPoint[0]) {
        azimuth = Math.PI * 2 - angle;
    } else if (endPoint[1] < startPoint[1] && endPoint[0] < startPoint[0]) {
        azimuth = angle;
    } else if (endPoint[1] < startPoint[1] && endPoint[0] >= startPoint[0]) {
        azimuth = Math.PI - angle;
    }
    return azimuth;
};

/**
 * 通过三个点获取方位角
 * @param pntA
 * @param pntB
 * @param pntC
 * @returns {number}
 */
export const getAngleOfThreePoints = (pntA: Point, pntB: Point, pntC: Point): number => {
    let angle: number = getAzimuth(pntB, pntA) - getAzimuth(pntB, pntC);
    return ((angle < 0) ? (angle + Math.PI * 2) : angle);
};

/**
 * 判断是否是顺时针
 * @param pnt1
 * @param pnt2
 * @param pnt3
 * @returns {boolean}
 */
export const isClockWise = (pnt1: Point, pnt2: Point, pnt3: Point): boolean => {
    return ((pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0]));
};

/**
 * 获取线上的点
 * @param t
 * @param startPnt
 * @param endPnt
 * @returns {[*,*]}
 */
export const getPointOnLine = (t: number, startPnt: Point, endPnt: Point): Point => {
    let x = startPnt[0] + (t * (endPnt[0] - startPnt[0]));
    let y = startPnt[1] + (t * (endPnt[1] - startPnt[1]));
    return [x, y];
};

/**
 * 获取立方值
 * @param t
 * @param startPnt
 * @param cPnt1
 * @param cPnt2
 * @param endPnt
 * @returns {[*,*]}
 */
export const getCubicValue = (t: number, startPnt: Point, cPnt1: Point, cPnt2: Point, endPnt: Point): Point => {
    t = Math.max(Math.min(t, 1), 0);
    let [tp, t2]: [tp: number, t2: number] = [(1 - t), (t * t)];
    let t3: number = t2 * t;
    let tp2: number = tp * tp;
    let tp3: number = tp2 * tp;
    let x: number = (tp3 * startPnt[0]) + (3 * tp2 * t * cPnt1[0]) + (3 * tp * t2 * cPnt2[0]) + (t3 * endPnt[0]);
    let y: number = (tp3 * startPnt[1]) + (3 * tp2 * t * cPnt1[1]) + (3 * tp * t2 * cPnt2[1]) + (t3 * endPnt[1]);
    return [x, y];
};

/**
 * 根据起止点和旋转方向求取第三个点
 * @param startPnt
 * @param endPnt
 * @param angle
 * @param distance
 * @param clockWise
 * @returns {[*,*]}
 */
export const getThirdPoint = (startPnt: Point, endPnt: Point, angle: number, distance: number, clockWise: boolean): Point => {
    let azimuth: number = getAzimuth(startPnt, endPnt);
    let alpha: number = clockWise ? (azimuth + angle) : (azimuth - angle);
    let dx: number = distance * Math.cos(alpha);
    let dy: number = distance * Math.sin(alpha);
    return ([endPnt[0] + dx, endPnt[1] + dy]);
};

/**
 * 函数继承
 * @param childCtor
 * @param parentCtor
 */
export const inherits = (childCtor: any, parentCtor: any): void => {
    /** @constructor */
    function TempCtor() {
    }

    TempCtor.prototype = parentCtor.prototype;
    childCtor.superClass_ = parentCtor.prototype;
    // @ts-ignore
    childCtor.prototype = new TempCtor();
    /** @override */
    childCtor.prototype.constructor = childCtor;
    childCtor.base = function (me: any, methodName: any, varArgs: any) {
        let args = Array.prototype.slice.call(arguments, 2);
        return parentCtor.prototype[methodName].apply(me, args);
    };
};

/**
 * 插值弓形线段点
 * @param center
 * @param radius
 * @param startAngle
 * @param endAngle
 * @returns {null}
 */
export const getArcPoints = (center: Point, radius: number, startAngle: number, endAngle: number): Array<Point> => {
    let [x, y, pnts, angleDiff]: [x: number, y: number, pnts: Array<Point>, angleDiff: number] = [0, 0, [], (endAngle - startAngle)];
    angleDiff = ((angleDiff < 0) ? (angleDiff + (Math.PI * 2)) : angleDiff);
    for (let i: number = 0; i <= 100; i++) {
        let angle = startAngle + angleDiff * i / 100;
        x = center[0] + radius * Math.cos(angle);
        y = center[1] + radius * Math.sin(angle);
        pnts.push([x, y]);
    }
    return pnts;
};

/**
 * getBisectorNormals
 * @param t
 * @param pnt1
 * @param pnt2
 * @param pnt3
 * @returns {[*,*]}
 */
export const getBisectorNormals = (t: number, pnt1: Point, pnt2: Point, pnt3: Point): Array<Point> => {
    let normal: Point = getNormal(pnt1, pnt2, pnt3);
    let [bisectorNormalRight, bisectorNormalLeft, dt, x, y]: [bisectorNormalRight: Point, bisectorNormalLeft: Point, dt: number, x: number, y: number] = [[0, 0], [0, 0], 0, 0, 0];
    let dist: number = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
    let uX: number = normal[0] / dist;
    let uY: number = normal[1] / dist;
    let d1: number = mathDistance(pnt1, pnt2);
    let d2: number = mathDistance(pnt2, pnt3);
    if (dist > Constants.ZERO_TOLERANCE) {
        if (isClockWise(pnt1, pnt2, pnt3)) {
            dt = t * d1;
            x = pnt2[0] - dt * uY;
            y = pnt2[1] + dt * uX;
            bisectorNormalRight = [x, y];
            dt = t * d2;
            x = pnt2[0] + dt * uY;
            y = pnt2[1] - dt * uX;
            bisectorNormalLeft = [x, y];
        } else {
            dt = t * d1;
            x = pnt2[0] + dt * uY;
            y = pnt2[1] - dt * uX;
            bisectorNormalRight = [x, y];
            dt = t * d2;
            x = pnt2[0] - dt * uY;
            y = pnt2[1] + dt * uX;
            bisectorNormalLeft = [x, y];
        }
    } else {
        x = pnt2[0] + t * (pnt1[0] - pnt2[0]);
        y = pnt2[1] + t * (pnt1[1] - pnt2[1]);
        bisectorNormalRight = [x, y];
        x = pnt2[0] + t * (pnt3[0] - pnt2[0]);
        y = pnt2[1] + t * (pnt3[1] - pnt2[1]);
        bisectorNormalLeft = [x, y];
    }
    return [bisectorNormalRight, bisectorNormalLeft];
};

/**
 * 获取默认三点的内切圆
 * @param pnt1
 * @param pnt2
 * @param pnt3
 * @returns {[*,*]}
 */
export const getNormal = (pnt1: Point, pnt2: Point, pnt3: Point): Point => {
    let dX1: number = pnt1[0] - pnt2[0];
    let dY1: number = pnt1[1] - pnt2[1];
    let d1: number = Math.sqrt(dX1 * dX1 + dY1 * dY1);
    dX1 /= d1;
    dY1 /= d1;
    let dX2: number = pnt3[0] - pnt2[0];
    let dY2: number = pnt3[1] - pnt2[1];
    let d2: number = Math.sqrt(dX2 * dX2 + dY2 * dY2);
    dX2 /= d2;
    dY2 /= d2;
    let uX: number = dX1 + dX2;
    let uY: number = dY1 + dY2;
    return [uX, uY];
};

/**
 * 获取左边控制点
 * @param controlPoints
 * @param t
 * @returns {[*,*]}
 */
export const getLeftMostControlPoint = (controlPoints: Array<Point>, t: number): Point => {
    const pnt1: Point = controlPoints[0];
    const pnt2: Point = controlPoints[1];
    const pnt3: Point = controlPoints[2];
    let controlX: number;
    let controlY: number;
    let pnts: Array<Point> = getBisectorNormals(0, pnt1, pnt2, pnt3);
    let normalRight: Point = pnts[0];
    let normal: Point = getNormal(pnt1, pnt2, pnt3);
    let dist: number = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
    if (dist > Constants.ZERO_TOLERANCE) {
        let mid: Point = Mid(pnt1, pnt2);
        let pX: number = pnt1[0] - mid[0];
        let pY: number = pnt1[1] - mid[1];
        let d1: number = mathDistance(pnt1, pnt2);
        let n: number = 2.0 / d1;
        let nX: number = -n * pY;
        let nY: number = n * pX;
        let a11: number = nX * nX - nY * nY;
        let a12: number = 2 * nX * nY;
        let a22: number = nY * nY - nX * nX;
        let dX: number = normalRight[0] - mid[0];
        let dY: number = normalRight[1] - mid[1];
        controlX = mid[0] + a11 * dX + a12 * dY;
        controlY = mid[1] + a12 * dX + a22 * dY;
    } else {
        controlX = pnt1[0] + t * (pnt2[0] - pnt1[0]);
        controlY = pnt1[1] + t * (pnt2[1] - pnt1[1]);
    }
    return [controlX, controlY];
};

/**
 * 获取右边控制点
 * @param controlPoints
 * @param t
 * @returns {[*,*]}
 */
export const getRightMostControlPoint = (controlPoints: Array<Point>, t: number): Point => {
    let count: number = controlPoints.length;
    let pnt1: Point = controlPoints[count - 3];
    let pnt2: Point = controlPoints[count - 2];
    let pnt3: Point = controlPoints[count - 1];
    let pnts: Array<Point> = getBisectorNormals(0, pnt1, pnt2, pnt3);
    let normalLeft: Point = pnts[1];
    let normal: Point = getNormal(pnt1, pnt2, pnt3);
    let dist: number = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
    let controlX: number;
    let controlY: number;
    if (dist > Constants.ZERO_TOLERANCE) {
        let mid: Point = Mid(pnt2, pnt3);
        let pX: number = pnt3[0] - mid[0];
        let pY: number = pnt3[1] - mid[1];
        let d1: number = mathDistance(pnt2, pnt3);
        let n: number = 2.0 / d1;
        let nX: number = -n * pY;
        let nY: number = n * pX;
        let a11: number = nX * nX - nY * nY;
        let a12: number = 2 * nX * nY;
        let a22: number = nY * nY - nX * nX;
        let dX: number = normalLeft[0] - mid[0];
        let dY: number = normalLeft[1] - mid[1];
        controlX = mid[0] + a11 * dX + a12 * dY;
        controlY = mid[1] + a12 * dX + a22 * dY;
    } else {
        controlX = pnt3[0] + t * (pnt2[0] - pnt3[0]);
        controlY = pnt3[1] + t * (pnt2[1] - pnt3[1]);
    }
    return [controlX, controlY];
};

/**
 * 插值曲线点
 * @param t
 * @param controlPoints
 * @returns {null}
 */
export const getCurvePoints = (t: number, controlPoints: Array<Point>): Array<Point> => {
    let leftControl: Point = getLeftMostControlPoint(controlPoints, t);
    let pnt1: Point;
    let pnt2: Point;
    let pnt3: Point;
    let normals: Array<Point> = [leftControl];
    let points: Array<Point> = [];
    for (let i: number = 0; i < controlPoints.length - 2; i++) {
        [pnt1, pnt2, pnt3] = [controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]];
        let normalPoints: Array<Point> = getBisectorNormals(t, pnt1, pnt2, pnt3);
        normals = normals.concat(normalPoints);
    }
    let rightControl = getRightMostControlPoint(controlPoints, t);
    if (rightControl) {
        normals.push(rightControl);
    }
    for (let i: number = 0; i < controlPoints.length - 1; i++) {
        pnt1 = controlPoints[i];
        pnt2 = controlPoints[i + 1];
        points.push(pnt1);
        for (let t: number = 0; t < Constants.FITTING_COUNT; t++) {
            let pnt: Point = getCubicValue(t / Constants.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2);
            points.push(pnt);
        }
        points.push(pnt2);
    }
    return points;
};

/**
 * 贝塞尔曲线
 * @param points
 * @returns {*}
 */
export const getBezierPoints = (points: Array<Point>): Array<Point> => {
    if (points.length <= 2) {
        return points;
    } else {
        let bezierPoints: Array<Point> = [];
        let n: number = points.length - 1;
        for (let t: number = 0; t <= 1; t += 0.01) {
            let [x, y]: [x: number, y: number] = [0, 0];
            for (let index: number = 0; index <= n; index++) {
                let factor: number = getBinomialFactor(n, index);
                let a: number = Math.pow(t, index);
                let b: number = Math.pow((1 - t), (n - index));
                x += factor * a * b * points[index][0];
                y += factor * a * b * points[index][1];
            }
            bezierPoints.push([x, y]);
        }
        bezierPoints.push(points[n]);
        return bezierPoints;
    }
};

/**
 * 获取阶乘数据
 * @param n
 * @returns {number}
 */
export const getFactorial = (n: number): number => {
    let result = 1;
    switch (n) {
        case 1:
            result = 1;
            break;
        case 2:
            result = 2;
            break;
        case 3:
            result = 6;
            break;
        case 24:
            result = 24;
            break;
        case 5:
            result = 120;
            break;
        default:
            for (let i: number = 1; i <= n; i++) {
                result *= i;
            }
            break;
    }
    return result;
};

/**
 * 获取二项分布
 * @param n
 * @param index
 * @returns {number}
 */
export const getBinomialFactor = (n: number, index: number): number => {
    return (getFactorial(n) / (getFactorial(index) * getFactorial(n - index)));
};

/**
 * 插值线性点
 * @param points
 * @returns {*}
 */
export const getQBSplinePoints = (points: Array<Point>): Array<Point> => {
    if (points.length <= 2) {
        return points;
    } else {
        let n: number = 2;
        let bSplinePoints: Array<Point> = [];
        let m: number = points.length - n - 1;
        bSplinePoints.push(points[0]);
        for (let i: number = 0; i <= m; i++) {
            for (let t: number = 0; t <= 1; t += 0.05) {
                let [x, y]: [x: number, y: number] = [0, 0];
                for (let k: number = 0; k <= n; k++) {
                    let factor: number = getQuadricBSplineFactor(k, t);
                    x += factor * points[i + k][0];
                    y += factor * points[i + k][1];
                }
                bSplinePoints.push([x, y]);
            }
        }
        bSplinePoints.push(points[points.length - 1]);
        return bSplinePoints;
    }
};

/**
 * 得到二次线性因子
 * @param k
 * @param t
 * @returns {number}
 */
export const getQuadricBSplineFactor = (k: number, t: number): number => {
    let res: number = 0;
    if (k === 0) {
        res = Math.pow(t - 1, 2) / 2;
    } else if (k === 1) {
        res = (-2 * Math.pow(t, 2) + 2 * t + 1) / 2;
    } else if (k === 2) {
        res = Math.pow(t, 2) / 2;
    }
    return res;
};

/**
 * 获取id
 * @returns {*|string|!Array.<T>}
 */
export const getuuid = (): string => {
    let [s, hexDigits]: [s: Array<any>, hexDigits: string] = [[], '0123456789abcdef'];
    for (let i: number = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4';
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23] = '-';
    return (s.join(''));
};

/**
 * 添加标识
 * @param obj
 * @returns {*}
 */
export const stamp = function (obj: any): any {
    let key = '_event_id_';
    obj[key] = obj[key] || (getuuid());
    return obj[key];
};

/**
 * 去除字符串前后空格
 * @param str
 * @returns {*}
 */
export const trim = (str: string): string => {
    return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
};

/**
 * 将类名截取成数组
 * @param str
 * @returns {Array|*}
 */
export const splitWords = (str: string): Array<string> => {
    return trim(str).split(/\s+/);
};

/**
 * 判断是否为对象
 * @param value
 * @returns {boolean}
 */
export const isObject = (value: any): any => {
    const type = typeof value;
    return value !== null && (type === 'object' || type === 'function');
};

/**
 * merge
 * @param a
 * @param b
 * @returns {*}
 */
export const merge = (a: any, b: any) => {
    for (const key in b) {
        if (isObject(b[key]) && isObject(a[key])) {
            merge(a[key], b[key]);
        } else {
            a[key] = b[key];
        }
    }
    return a;
};

export function preventDefault(e: any) {
    e = e || window.event;
    if (e.preventDefault) {
        e.preventDefault();
    } else {
        e.returnValue = false;
    }
}